﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Xml;
using BizMock.Adapter.Runtime;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.XLANGs.BaseTypes;
using System.Data.SqlClient;

namespace BizMock
{
    public sealed class Expect
    {
        public static int DELAY = 1500;
        public static int TIMEOUT = 5000;

        private static ExpectInfo _expectInfo = new ExpectInfo();

        internal class ExpectInfo
        {
            internal ExpectInfo()
            {
                Count = 0;
                MaxCount = int.MaxValue;
                MinCount = 0;
                TimeOut = TIMEOUT;
                Messages = new List<string[]>();
                TransformedMessages = new List<string[]>();
            }

            internal enum TypeOfMessage { Request, Response, File };

            internal int Count { get; set; }

            internal int MaxCount { get; set; }

            internal int MinCount { get; set; }

            internal MessageVerifierBase MessageVerifier { get; set; }

            internal string Uri { get; set; }

            internal TypeOfMessage MessageType { get; set; }

            internal int TimeOut { get; set; }

            internal List<string[]> Messages { get; set; }

            internal string EventLogSource { get; set; }

            internal List<string[]> TransformedMessages { get; set; }

            internal TransformBase Map { get; set; }

            internal string SearchPattern { get; set; }

            internal string DBresult { get; set; }

            internal string DBquery { get; set; }

            internal string DBconnection { get; set; }

        }

        private static string executeQuery()
        {
            SqlConnection sqlConn = new SqlConnection(_expectInfo.DBconnection);
            SqlCommand sqlCmd = new SqlCommand(_expectInfo.DBquery);
            sqlCmd.Connection = sqlConn;
            string xmlOutput = string.Empty;
            try
            {
                sqlCmd.Connection.Open();
                XmlReader xr = sqlCmd.ExecuteXmlReader();
                XmlDocument xdoc = new XmlDocument();
                xdoc.Load(xr);
                xmlOutput = xdoc.DocumentElement.OuterXml;

            }
            finally
            {
                sqlCmd.Connection.Close();
            }
            return xmlOutput;
        }

        private static void verifyDBResult()
        {
            string xmlOutput = executeQuery();
            bool verified = _expectInfo.DBresult == xmlOutput;
            if (!verified && _expectInfo.TimeOut > 0)
            {
                Thread.Sleep(_expectInfo.TimeOut);
                xmlOutput = executeQuery();
                verified = _expectInfo.DBresult == xmlOutput;
            }
            if (!verified)
                throw new BizMockException(string.Format("expected dbresult {0} received {1}", _expectInfo.DBresult, xmlOutput));
        }

        private static void verifyReceivedFiles()
        {
            TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, _expectInfo.TimeOut);
            long beginTicks = DateTime.Now.Ticks;
            long elapsedTicks = 0;
            bool timedOut = false;
            bool exit = false;
            int count;
            do
            {
                Thread.Sleep(DELAY);
                elapsedTicks = DateTime.Now.Ticks - beginTicks;
                timedOut = waitTime.Ticks < elapsedTicks;
                count = BizMockery.Instance.GetBizMockContextFiles(_expectInfo.Uri, _expectInfo.MessageType.ToString()).Length;
                exit = isExpectedFileCount(count) || timedOut;
            } while (!exit);
            if (!isExpectedFileCount(count))
                throw new BizMockException(string.Format("{0} messages not found at {1}", _expectInfo.Count, _expectInfo.Uri));
        }

        private static void verifyMessageContents()
        {
            Expect._expectInfo.Messages = BizMockery.Instance.GetBizMockMessageFiles(_expectInfo.Uri, _expectInfo.MessageType.ToString());
            foreach (string[] files in _expectInfo.Messages)
            {
                BizMockery.Instance.ValidateMessageContents(files, _expectInfo.MessageVerifier);
                deleteBizMockMessage(files[0]);
            }
        }

        private static void deleteBizMockMessage(string file)
        {
            int i = file.IndexOf("_part");
            string ctrlFile = file.Substring(0, i).Replace("msg", "ctrl") + ".xml";
            MessageFileHelper.RemoveBizMockMessage(ctrlFile);
        }

        private static void verifyFileContents()
        {
            string[] files = Directory.GetFiles(_expectInfo.Uri, _expectInfo.SearchPattern);

            BizMockery.Instance.ValidateMessageContents(files, _expectInfo.MessageVerifier);
        }

        private static void verifyEventLog()
        {
            BizMockery.Instance.ValidateEventLog(_expectInfo.EventLogSource, _expectInfo.MessageVerifier);
        }

        private static void transformFiles()
        {
            foreach (string[] files in _expectInfo.Messages)
            {
                _expectInfo.TransformedMessages.Add(BizMockery.Instance.Transform(files, _expectInfo.Map));
            }
        }

        private static void verifyTransformedMessageContents()
        {
            foreach (string[] files in _expectInfo.TransformedMessages)
            {
                BizMockery.Instance.ValidateMessageContents(files, _expectInfo.MessageVerifier);
            }
        }

        private static bool isExpectedFileCount(int fileCount)
        {
            return fileCount <= _expectInfo.MaxCount && fileCount >= _expectInfo.MinCount && (_expectInfo.Count == 0 || _expectInfo.Count == fileCount);
        }

        private static void getResponse()
        {
            BizMockery.Instance.GetResponse(_expectInfo.Uri);
        }

        public static EventLogVerifyInfo EventLogSource(string source)
        {
            _expectInfo.EventLogSource = source;
            return new EventLogVerifyInfo();
        }

        public static EventLogVerifyInfo EventLogSource(EventLogArtifact evLog)
        {
            _expectInfo.EventLogSource = evLog.Source;
            return new EventLogVerifyInfo();
        }

        public static MessageTypeInfo One
        {
            get
            {
                _expectInfo = new ExpectInfo();
                _expectInfo.Count = 1;
                return new MessageTypeInfo();
            }
        }

        public static MessageTypeInfo AsMuch(int count)
        {
            _expectInfo = new ExpectInfo();
            _expectInfo.MaxCount = count;
            return new MessageTypeInfo();
        }

        public static MessageTypeInfo AtLeast(int count)
        {
            _expectInfo = new ExpectInfo();
            _expectInfo.MinCount = count;
            return new MessageTypeInfo();
        }

        public static MessageTypeInfo Exactly(int count)
        {
            _expectInfo = new ExpectInfo();
            _expectInfo.Count = count;
            return new MessageTypeInfo();
        }

        public static MessageVerifyInfo Message(params string[] files)
        {
            _expectInfo.Messages.Add(files);
            return new MessageVerifyInfo();
        }

        public static MessageVerifyInfo Message(BizMockMessage message)
        {
            _expectInfo.Messages.Add(message.Files);
            return new MessageVerifyInfo();
        }

        public static ForQueryInfo DBResult(string dbresult)
        {
            _expectInfo.DBresult = dbresult;
            return new ForQueryInfo();
        }

        public sealed class ForQueryInfo
        {
            public DatabaseInfo ForQuery(string query)
            {
                _expectInfo.DBquery = query;
                return new DatabaseInfo();
            }
        }

        public sealed class DatabaseInfo
        {
            public void AtDatabase(string connectionString, int timeOut)
            {
                _expectInfo.TimeOut = timeOut;
                _expectInfo.DBconnection = connectionString;
                Expect.verifyDBResult();
            }

            public void AtDatabase(string connectionString)
            {
                AtDatabase(connectionString, 100);
            }
        }

        public sealed class MessageTypeInfo
        {

            public RequestAtInfo Request
            {
                get { return new RequestAtInfo(); }
            }

            public ResponseAtInfo Response
            {
                get { return new ResponseAtInfo(); }
            }

            public FileMatchingInfo File
            {
                get { return new FileMatchingInfo(); }
            }

        }

        public sealed class RequestAtInfo
        {
            public RequestAtInfo()
            {
                _expectInfo.MessageType = ExpectInfo.TypeOfMessage.Request;
            }

            public RequestVerifyInfo At(string uri)
            {
                _expectInfo.Uri = uri;
                return new RequestVerifyInfo();
            }

            public RequestVerifyInfo At(SendPortArtifact sp)
            {
                _expectInfo.Uri = sp.PortName;
                return new RequestVerifyInfo();
            }
        }

        public sealed class ResponseAtInfo
        {
            public ResponseAtInfo()
            {
                _expectInfo.MessageType = ExpectInfo.TypeOfMessage.Response;
            }

            public ResponseVerifyInfo At(string uri)
            {
                _expectInfo.Uri = uri;
                return new ResponseVerifyInfo();
            }

            public ResponseVerifyInfo At(RequestResponseLocationArtifact rrl)
            {
                _expectInfo.Uri = rrl.RclName;
                return new ResponseVerifyInfo();
            }

        }

        public sealed class FileMatchingInfo
        {
            public FileAtInfo Matching(string searchPattern)
            {
                _expectInfo.SearchPattern = searchPattern;
                return new FileAtInfo();
            }
        }

        public sealed class FileAtInfo
        {
            public FileAtInfo()
            {
                _expectInfo.MessageType = ExpectInfo.TypeOfMessage.File;
            }

            public FileVerifyInfo At(string path)
            {
                _expectInfo.Uri = path;
                return new FileVerifyInfo();
            }
        }

        public abstract class VerifyInfoBase
        {
            public abstract void Verify(MessageVerifierBase verifier);

            public void Verify(string regularExpression)
            {
                FlatMessageVerifier vrf = new FlatMessageVerifier();
                vrf.Match(regularExpression);
                Verify(vrf);
            }
        }

        public sealed class EventLogVerifyInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                Expect.verifyEventLog();
            }

        }

        public sealed class MessageVerifyInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                verifyMessageContents();
            }

            public VerifyMapInfo TransformedWith(TransformBase map)
            {
                _expectInfo.Map = map;
                transformFiles();
                return new VerifyMapInfo();
            }
        }

        public sealed class RequestVerifyInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                Expect.verifyReceivedFiles();
                Expect.verifyMessageContents();
            }

            public void Verify(string regularExpression)
            {
                FlatMessageVerifier flatVrf = new FlatMessageVerifier();
                flatVrf.Match(regularExpression);
                _expectInfo.MessageVerifier = flatVrf;
                Expect.verifyReceivedFiles();
                Expect.verifyMessageContents();
            }
        }

        public sealed class ResponseVerifyInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                Expect.getResponse();
                Expect.verifyReceivedFiles();
                Expect.verifyMessageContents();
            }
        }

        public sealed class FileVerifyInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                Expect.verifyFileContents();
            }
        }

        public sealed class VerifyMapInfo : VerifyInfoBase
        {
            public override void Verify(MessageVerifierBase verifier)
            {
                _expectInfo.MessageVerifier = verifier;
                verifyTransformedMessageContents();
            }
        }

    }
}